#define CURL_STATICLIB

#include <curl/curl.h>
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <thread>
using namespace std;

static size_t callback(void* ptr, size_t size, size_t nmemb, void* stream) {
	// 获取到的http body存放在ptr中，先将其转换为string格式
	std::string s((char*)ptr, size * nmemb);
	std::string* result = static_cast<std::string*>(stream);
	*result += s;
	return size * nmemb;
}
int https_get(const std::string & strUrl, std::string & strResponse, const char * pCaPath)
{
	CURLcode res;
	CURL* curl = curl_easy_init();
	if(NULL == curl)
	{
		return CURLE_FAILED_INIT;
	}
 
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
	//curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	std::string curl_result;
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_result);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
	if(NULL == pCaPath)
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);//设定为不验证证书和HOST
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
	}
	else
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
		curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);
	}
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 1L);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L);
	curl_easy_setopt(curl, CURLOPT_SERVER_RESPONSE_TIMEOUT, 1L);
	int max_retries = 1;
	int retry_count = 0;

	while (retry_count < max_retries) {
		res = curl_easy_perform(curl);
		if (res == CURLE_OK) {
			break;
		}
		else {
			std::cerr << "Request failed with error: " << curl_easy_strerror(res) << ". Retrying..." << std::endl;
			retry_count++;
			//std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待 2 秒后重试
		}
	}

	curl_easy_cleanup(curl);
	strResponse = curl_result;

	return res;
}
template <class Type>
Type stringToNum(const string& str)
{
	istringstream iss(str);
	Type num;
	iss >> num;
	return num;
}
vector<float> splitString(string str, char delimiter) {
	vector<float> result;
	stringstream ss(str);
	string item;
	while (getline(ss, item, delimiter)) {
		result.push_back(stringToNum<float>(item));
	}
	return result;
}
vector<string> splitString2(string str, char delimiter) {
	vector<string> result;
	stringstream ss(str);
	string item;
	while (getline(ss, item, delimiter)) {
		result.push_back(item);
	}
	return result;
}
vector<string> removeString(vector<string> vstr, char delimiter) {
	vector<string> result;
	for (auto str : vstr) {
		stringstream ss(str);
		string item;
		while (getline(ss, item, delimiter)) {
			result.push_back(item);
		}
	}
	return result;
}
std::vector<std::string> GetListFromFile(std::string filename) {
	std::fstream list;
	list.open(filename.c_str(), std::ios::in);
	std::vector<string> vList;
	string line;

	if (list.is_open())
		while (!list.eof())
		{
			getline(list, line);
			if (line != "")
				vList.push_back(line);
		}
	list.close();
	return vList;
}
void calculateCubeIndex(std::vector<std::vector<float>> &points) {
	for (auto point : points) {
		point[2] /= 256;
		point[3] /= 256;
		point[4] /= 256;
	}
}

struct Channel {
	string name;
	string url;
	int size[3];
	int cubesize[3];
	float space[3];
};
Channel parseCatalog(std::string catalog) {
	std::vector<string>vcatalog= splitString2(catalog,'\n');
	vcatalog=removeString(vcatalog,'\r');
	std::cout << vcatalog[0] << std::endl;
	Channel ch;
	for (auto item : vcatalog) {
		if (item == "[CH00_WEBM]" || item == "[CH00webm]")
		{
			ch.name = "CH00_WEBM";
		}
		if(ch.name!="CH00_WEBM") {
			continue;
		}
		if (item.find("pattern") != std::string::npos) {
			int poswebm = item.find("ch00webm");
			ch.url = item.substr(8, poswebm);
		}

		else if (item.find("cubesize") != std::string::npos) {
			int pos = item.find("=");
			string sizestr = item.substr(pos + 1);
			auto size = splitString(sizestr, ' ');
			ch.cubesize[0] = size[0];
			ch.cubesize[1] = size[1];
			ch.cubesize[2] = size[2];
		}
		else if (item.find("size") != std::string::npos) {
			int pos = item.find("=");
			string sizestr = item.substr(pos + 1);
			auto size = splitString(sizestr, ' ');
			ch.size[0] = size[0];
			ch.size[1] = size[1];
			ch.size[2] = size[2];
		}
		else if (item.find("direction") != std::string::npos) {
			int pos = item.find("=");
			string directionstr = item.substr(pos + 1);
			auto direction = splitString(directionstr, ' ');
			ch.space[0] = direction[0];
			ch.space[1] = direction[4];
			ch.space[2] = direction[8];
		}
	}
	return ch;
}
std::string intToString(int value, int width) {
	std::stringstream ss;
	ss << std::internal << std::setfill('0') << std::setw(width) << value;
	return ss.str();
}
void getcubes(Channel ch, std::vector<std::vector<float>> vPoints) {
	std::set<std::vector<int>> cubeIndexs;
	for (int i = 0; i < vPoints.size(); i++) {
		int index[3] = { vPoints[i][2] / ch.space[0],vPoints[i][3] / ch.space[1],vPoints[i][4] / ch.space[2] };
		//int indexCube[3] = { index[0] - index[0] % ch.cubesize[0],index[1] - index[1] % ch.cubesize[1] ,index[2] - index[2] % ch.cubesize[2] };
		int indexCube[3] = {
			((index[0] * 2 + ch.cubesize[0]) / ch.cubesize[0] - 2) / 2 * ch.cubesize[0],
			((index[1] * 2 + ch.cubesize[1]) / ch.cubesize[1] - 2) / 2 * ch.cubesize[1],
			((index[2] * 2 + ch.cubesize[2]) / ch.cubesize[2] - 2) / 2 * ch.cubesize[2]
		};
		cubeIndexs.insert({ indexCube[0],indexCube[1] ,indexCube[2] });
		for (int x = 0; x < 2; x++) {
			for (int y = 0; y < 2; y++) {
				for (int z = 0; z < 2; z++) {
					cubeIndexs.insert({ indexCube[0]+x*ch.cubesize[0],indexCube[1] + y * ch.cubesize[1] ,indexCube[2] + z * ch.cubesize[2] });
				}
			}
		}
	}
	// request first
	std::set<std::string> unfinishedURL;
	for (auto it = cubeIndexs.begin(); it != cubeIndexs.end(); it++) {
		std::string url = ch.url+"/z"+intToString((*it)[2],8)+"/y"+ intToString((*it)[1], 8) +".x"+ intToString((*it)[0], 8)+".webm";
		cout << url << endl;
		std::string res = "";
		if (https_get(url, res, NULL) != 0) {
			unfinishedURL.insert(url);
		}
	}

	//request tile finished
	while (unfinishedURL.size()) {
		cout << "try again" << endl;
		std::set<std::string> unfinishedURLtemp=unfinishedURL;
		unfinishedURL.clear();
		for (auto it = unfinishedURLtemp.begin(); it != unfinishedURLtemp.end(); it++) {
			cout << *it << endl;
			std::string res = "";
			if (https_get(*it, res, NULL) != 0) {
				unfinishedURL.insert(*it);
			}
		}
	}

}
int main(int argc, char* argv[]) {
	if (argc < 2)
	{
		std::cout << "Usage:\n  getcube -i filelist\n";
		return 1;
	}
	std::string filelist;
	for (int i = 1; i < argc; i++)
	{
		if (strcmp(argv[i], "-i") == 0)
		{
			filelist = argv[++i];
		}
		else {
			std::cout << "Usage:\n  getcube -i filelist\n";
			return 1;
		}

	}
	std::vector<string> vPointList = GetListFromFile(filelist);
	std::string catalog = vPointList[0];
	
	std::vector<std::vector<float>> vPoints;
	for (int i = 1; i < vPointList.size(); i++) {
		std::string string = vPointList[i];
		char split = ' ';
		vPoints.push_back(splitString(string,split));
		
	}
	calculateCubeIndex(vPoints);


	//get and parse catalog
	std::string url = "";
	url = splitString2(catalog,' ')[1];
	std::string res = "";
	https_get(url, res, NULL);
	Channel ch= parseCatalog(res);
	getcubes(ch, vPoints);
	return 0;
}
//#define CURL_STATICLIB
//
//#include <iostream>
//#include <string>
//#include <map>
//#include "curl/curl.h"
//
//// @brief curl发送http请求调用的回调函数，回调函数中对返回的json格式的body进行了解析，解析结果储存在result中
//// @param 参数定义见libcurl库文档
//// @return 返回值定义见libcurl库文档
//static size_t callback(void* ptr, size_t size, size_t nmemb, void* stream) {
//	// 获取到的http body存放在ptr中，先将其转换为string格式
//	std::string s((char*)ptr, size * nmemb);
//	std::string* result = static_cast<std::string*>(stream);
//	*result += s;
//	return size * nmemb;
//}
//
//int main() {
//	// 服务地址
//	// libcurl默认是不支持SSL的, 如果需要支持https，请在libcurl的编译选项中设置
//	std::string url = "https://10.10.31.25:8003/api/data/fmost241601/ch00webm/z00006059/y00010752.x00013824.webm";
//
//	// 发送请求
//	CURL* curl;
//	CURLcode result_code;
//	int error_code = 0;
//	curl = curl_easy_init();
//	if (curl) {
//		std::string curl_result;
//		curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
//		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
//		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
//		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_result);
//		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
//		result_code = curl_easy_perform(curl);
//		if (result_code != CURLE_OK) {
//			std::cout << "curl_easy_perform() failed: " << curl_easy_strerror(result_code) << std::endl;
//			return -1;
//		}
//		curl_easy_cleanup(curl);
//
//		std::cout << "返回结果为：" << std::endl;
//		std::cout << curl_result << std::endl;
//	}
//	return 0;
//}